import { T } from '@knno/i18n';
import type { Fn } from '@knno/jsx';
import time from '@knno/time';
import SVGDate from '@mdi/svg/svg/calendar-blank.svg';
import SVGDateTime from '@mdi/svg/svg/calendar-clock.svg';
import SVGDateRange from '@mdi/svg/svg/calendar-range.svg';
import SVGTime from '@mdi/svg/svg/clock-time-eight-outline.svg';
import { type DateValue, dateValue } from '../calendar/calendar';
import { type DateMode, DatePicker, type DatePickHandler } from '../datePicker/datePicker';
import { IconInput, type IconInputProps } from '../input/input';
import { DATA } from '../symbol';

type DateBoxData = {
	/**
	 * dy: accurate to year
	 * dm: accurate to month
	 * dd: accurate to day
	 * th: accurate to hour
	 * tm: accurate to minute
	 * ts: accurate to second
	 * dd+th: date accurate to day and time accurate to hour
	 * dd+tm: date accurate to day and time accurate to minute
	 * dd+ts: date accurate to day and time accurate to second
	 */
	mode: DateMode;
	/**
	 * If precision is hour, minute or second then force use roller as the picker.
	 */
	useRoller: boolean;
	isRange: boolean;
	selected: Date[];
	readOnly: boolean;
	popup?: DatePickHandler;
};

export type DateBoxProps = IconInputProps<Datebox> & {
	mode?: DateMode;
	dateValue?: Date[] | Date;
	roller?: boolean;
	range?: boolean;
	minDate?: Date;
	maxDate?: Date;
	/**
	 * If precision is hour, minute or second then force use roller as the picker.
	 */
	onDateChange?: (this: Datebox, value: Date[] | Date | null) => void;
};

export class Datebox extends IconInput<DateBoxProps, Datebox> {
	private [DATA]: DateBoxData;
	constructor(props: DateBoxProps) {
		/* eslint-disable */
		const {
			value,
			defaultValue,
			autoComplete,
			inputMode,
			suffixIcon,
			maxLength,
			minLength,
			pattern,
			min,
			max,
			...newProps
		} = props;
		/* eslint-enable */
		newProps.readOnly = true;
		newProps.type = 'text';
		super(newProps);
		this[DATA] = {
			mode: props.mode ?? 'dd',
			useRoller: props.roller ?? false,
			isRange: props.range ?? false,
			selected: [],
			readOnly: props.readOnly ?? false,
		};
		super.setReadOnly(true);
		this.setMode(props.mode ?? 'dd');
		if (props.dateValue) this.setDateValue(props.dateValue);
		this.el.addEventListener('click', () => {
			this.openSelect();
		});
		this.el.addEventListener('keyup', (evt) => {
			if (evt.key === 'Space' || evt.key === ' ') {
				this.openSelect();
			}
		});
		super.el.classList.add('select');
		if (props.onClear) {
			this.set('onClear', props.onClear);
		}
	}

	private getText(): string {
		if (this[DATA].selected.length === 0) return '';
		let pattern: string;
		if (this[DATA].mode === 'dy') {
			pattern = 'yyyy';
		} else if (this[DATA].mode === 'dm') {
			pattern = 'yyyy-MM';
		} else if (this[DATA].mode === 'dd') {
			pattern = 'yyyy-MM-dd';
		} else if (this[DATA].mode === 'dd+th') {
			pattern = 'yyyy-MM-dd HH';
		} else if (this[DATA].mode === 'dd+tm') {
			pattern = 'yyyy-MM-dd HH:mm';
		} else if (this[DATA].mode === 'dd+ts') {
			pattern = 'yyyy-MM-dd HH:mm:ss';
		} else if (this[DATA].mode === 'th') {
			pattern = 'HH';
		} else if (this[DATA].mode === 'tm') {
			pattern = 'HH:mm';
		} else {
			pattern = 'HH:mm:ss';
		}
		if (this[DATA].isRange) {
			const d1 = time.format(this[DATA].selected[0], pattern);
			const d2 = time.format(this[DATA].selected[1], pattern);
			if (d1 === null || d2 === null) return '';
			if (d1 === d2) {
				return d1;
			} else {
				return `${d1}  ${T`~`}  ${d2}`;
			}
		} else {
			return time.format(this[DATA].selected[0], pattern) ?? '';
		}
	}

	clear(): this {
		this.setDateValue(null);
		return this;
	}

	private setSuffix() {
		if (this.range) {
			this.setSuffixIcon(SVGDateRange);
		} else if (this.mode.startsWith('t')) {
			this.setSuffixIcon(SVGTime);
		} else if (this.mode.includes('+')) {
			this.setSuffixIcon(SVGDateTime);
		} else {
			this.setSuffixIcon(SVGDate);
		}
	}

	openSelect() {
		if (this.readOnly || this.disabled || this[DATA].popup) return;
		this[DATA].popup = DatePicker.show({
			refer: this.el,
			date: this[DATA].selected,
			range: this[DATA].isRange,
			mode: this[DATA].mode,
			minDate: this.minDate,
			maxDate: this.maxDate,
			focusOnSelect: this.input,
		})
			.onSelect((value) => {
				this.setDateValue(value);
				this.setCustomValidity('');
				this.input.dispatchEvent(new Event('change'));
				this.call('onDateChange', value);
				this.input.focus();
			})
			.onClose(() => {
				this[DATA].popup = undefined;
			});
	}

	get roller(): boolean {
		return this[DATA].useRoller;
	}
	setRoller(value: boolean): this {
		this[DATA].useRoller = value;
		return this;
	}

	get range(): boolean {
		return this[DATA].isRange;
	}
	setRange(value: boolean): this {
		this[DATA].isRange = value;
		this.setSuffix();
		super.setValue(this.getText());
		return this;
	}

	get minDate(): Date | undefined {
		return this.props.minDate;
	}

	setMinDate(value: Date | undefined): this {
		this.props.minDate = value;
		return this;
	}

	get maxDate(): Date | undefined {
		return this.props.maxDate;
	}
	setMaxDate(value: Date | undefined): this {
		this.props.maxDate = value;
		return this;
	}

	get mode(): DateMode {
		return this[DATA].mode;
	}
	setMode(value: DateMode): this {
		this[DATA].mode = value;
		this.setSuffix();
		super.setValue(this.getText());
		return this;
	}

	get dateValue(): Date[] | Date | null {
		return structuredClone(dateValue(this[DATA]));
	}
	setDateValue(val: DateValue[] | DateValue | null): this {
		dateValue(this[DATA], val);
		super.setValue(this.getText());
		return this;
	}

	override setValue(_: string): this {
		return this;
	}
	override setInputMode(_: string): this {
		return this;
	}
	override setAutoComplete(_: string): this {
		return this;
	}
	override setMaxLength(_: number): this {
		return this;
	}
	override setMinLength(_: number): this {
		return this;
	}
	override setMin(_: string): this {
		return this;
	}

	override setMax(_: string): this {
		return this;
	}
	override setPattern(_: string): this {
		return this;
	}
	override setType(_: string): this {
		return this;
	}
	override get readOnly(): boolean {
		return this[DATA].readOnly;
	}
	override setReadOnly(value: boolean): this {
		this[DATA].readOnly = value;
		return this;
	}

	override set<
		K extends string | number,
		S = K extends keyof Fn<DateBoxProps> ? DateBoxProps[K] : never,
	>(key: K, value: S): this;
	override set<K extends keyof Fn<DateBoxProps>>(key: K, value: DateBoxProps[K]): this;
	override set<K extends keyof Fn<DateBoxProps>>(key: K, value: DateBoxProps[K]): this {
		if (key === 'onClear') {
			super.set('onClear', () => {
				this.setDateValue(null);
				(value as Fn<DateBoxProps>['onClear'])?.call(this);
			});
			return this;
		} else return super.set(key, value);
	}
}
